import ValuePair from "./ValuePair";
import isEmpty from "../utils/isEmpty";

interface DictionaryType<TK, TV> {
  /**
   * 存储结构
   */
  table: Array<ValuePair<TK, TV>>;

  /**
   * 设置和更新
   * @param key
   * @param value
   */
  set(key: TK, value: TV): void;

  /**
   * 删除指定的key并且返回删除的内容
   * @param key
   */
  remove(key: TK): ValuePair<TK, TV> | undefined;

  /**
   * 判断是否拥有某个key
   * @param key
   */
  has(key: TK): boolean;

  /**
   * 获取某个key对应的值
   * @param key
   */
  get(key: TK): ValuePair<TK, TV> | undefined;

  /**
   * 清空字典
   */
  clear(): void;

  /**
   * 获取字典的长度
   */
  size(): number;

  /**
   * 是否为空
   */
  isEmpty(): boolean;

  /**
   * 获取全部的key
   */
  keys(): Array<TK>;

  /**
   * 获取全部的value
   */
  values(): Array<TV>;

  /**
   * 以 [[key,value]] 形式返回全部存储的内容
   */
  keyValues(): Array<[TK, TV]>;

  /**
   * 遍历整个字典
   * @param callbackFn 回调函数, 返回false停止循环
   */
  forEach(callbackFn: (key: TK, value: TV) => boolean | void): void;
}


export default class Dictionary<TK, TV> implements DictionaryType<TK, TV> {
  table: Array<ValuePair<TK, TV>>;

  constructor() {
    this.table = []
  }

  clear(): void {
    this.table = []
  }

  forEach(callbackFn: (key: TK, value: TV) => (boolean | void)): void {
    let len = this.table.length;
    for (let i = 0; i < len; i++) {
      let item = this.table[i];
      let flag = callbackFn(item.key, item.value);
      if (flag === false) {
        break;
      }
    }
  }

  get(key: TK): ValuePair<TK, TV> | undefined {
    if (this.has(key)) {
      const index = this.getItemIndex(key);
      return this.table[index]
    }
    return undefined;
  }

  has(key: TK): boolean {
    return this.getItemIndex(key) !== -1;
  }

  private getItemIndex(key: TK): number {
    for (let i = 0, len = this.table.length; i < len; i++) {
      if (this.table[i].key === key) {
        return i
      }
    }
    return -1;
  }

  isEmpty(): boolean {
    return !(this.size());
  }

  keyValues(): Array<[TK, TV]> {
    return this.table.map(item => [item.key, item.value]);
  }

  keys(): Array<TK> {
    return this.table.map(item => item.key);
  }

  remove(key: TK): ValuePair<TK, TV> | undefined {
    if (this.has(key)) {
      let index = this.getItemIndex(key);
      return this.table.splice(index, 1)[0]
    }
    return undefined;
  }

  set(key: TK, value: TV): void {
    if (isEmpty(key)) {
      throw new Error("key is required")
    }

    if (isEmpty(value)) {
      throw new Error("value is required")
    }
    if (this.has(key)) {
      const index = this.getItemIndex(key);
      this.table[index].value = value;
    } else {
      const new_node = new ValuePair(key, value);
      this.table.push(new_node)
    }
  }

  size(): number {
    return this.table.length;
  }

  values(): Array<TV> {
    return this.table.map(item => item.value);
  }

  toString(): string {
    let str = ""
    for (let i = 0, len = this.table.length; i < len; i++) {
      str += this.table[i].toString()
      str += ","
    }
    str = str.substr(0, str.length - 1)
    return str
  }
}
//
// type dic = Dictionary<number, number>
// const dictionary: dic = new Dictionary();
//
// function dic_log() {
//   console.log("size", dictionary.size());
//   console.log("has", dictionary.has(1));
//   console.log("isEmpty", dictionary.isEmpty());
//   console.log("keys", dictionary.keys());
//   console.log("values", dictionary.values());
//   console.log("keyValues", dictionary.keyValues())
//   console.log("toString", dictionary.toString());
//   console.log("#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-");
// }
//
// dic_log();
// dictionary.set(1, 1)
// dic_log();
// dictionary.set(2, 1)
// dic_log();
// dictionary.set(3, 1)
// dic_log();
// dictionary.set(4, 1)
// dic_log();
// console.log("forEach---------------start")
//
// dictionary.forEach((key, value) => {
//   console.log(`forEach: key = ${key} ------  value = ${value}`)
// })
// console.log("forEach---------------end")
//
// console.log(dictionary.remove(3));
// dic_log();
// console.log(dictionary.remove(2));
// dic_log();
